/**************************************************************************(C)*
 *
 *  This file is part of the GreenHills project
 *  Copyright (C) 2009  Tieto Corporation
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  Not for commmercial use. Only with Tieto permission.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *************************************************************************(C)*/
#include "stub.h"

#include "server.h"
#include "../net/msgserversocketconnection.h"

#include "../event/loginevent.h"
#include "../event/logoutevent.h"
#include "../event/createroomevent.h"
#include "../event/joinroomevent.h"
#include "../event/leaveroomevent.h"
#include "../event/additemevent.h"
#include "../event/removeitemevent.h"
#include "../event/updateobjectevent.h"
#include "../event/identificationevent.h"

#include "../util/binstream.h"
#include "../util/memstreambuf.h"

#include <assert.h>

using namespace greenhills::core;
using namespace greenhills::event;
using namespace greenhills::server;
using namespace greenhills::net;
using namespace greenhills::io;
using namespace greenhills::util;


CStub::CStub(CServer &pServer)
        : mServer(pServer),
          mServerSync(new CMsgServerSocketConnection()),
          mServerAsync(new CMsgServerSocketConnection())
{
    mServerSync->addDataListener(this);
    mServerAsync->addDataListener(this);

}

void CStub::open()
{
    mServerSync->listen(SERVER_PORT_SYNC);
    mServerAsync->listen(SERVER_PORT_ASYNC);
}

void CStub::close()
{
    mServerSync->close();
    mServerAsync->close();
}

void CStub::process()
{
    mServerSync->process();
    mServerAsync->process();
}

void CStub::dataReceived(const char *pData, unsigned int pLength,
                                     CConnection *pConnection)
{
    memstreambuf buffer(pData, pLength);
    ibinstream is(&buffer);
    CEvent *event = CEvent::deserialize(is);
    if (event == 0)
    {
        std::cout << "Unknown message type" << std::endl;
        return;
    }

    const CActionEvent *actionRequest =
        dynamic_cast<const CActionEvent*>(event);

    if (actionRequest != 0)
    {
        actionRequestReceived(actionRequest, pConnection);
    }
    else
    {
        eventReceived(event, pConnection);
    }

    delete event;
}

void CStub::actionRequestReceived(const CActionEvent *pActionRequest,
                                        CConnection *pConnection)
{
    switch(pActionRequest->type())
    {
    case CEvent::LoginRequest:
    {
        const CLoginRequestEvent *loginRequestEvent =
                dynamic_cast<const CLoginRequestEvent*>(pActionRequest);

        greenhills::core::CClientId clientId =
                mServer.login(loginRequestEvent->username(),
                              loginRequestEvent->password());

        std::cout << "Server Stub: Login Request received" << std::endl;
        std::cout << "   ClientId: " << clientId << std::endl;

        if (clientId)
        {
            CClient *client = mServer.clients().client(clientId);
            assert(client);
            client->assignActionConnection(pConnection);
            std::cout << "Action Channel Assigned" << std::endl;
        }

        CLoginResponseEvent response(pActionRequest->transactionId(), clientId);
        CClient::send(pConnection, &response);
        break;
    }
    case CEvent::LogoutRequest:
    {
        const CLogoutRequestEvent *logoutRequestEvent =
                dynamic_cast<const CLogoutRequestEvent*>(pActionRequest);

        std::cout << "Server Stub: Logout Request received" << std::endl;
        std::cout << "   ClientId: " << logoutRequestEvent->clientId()
                << std::endl;

        CClient *client = mServer.clients().client(pConnection);
        assert(client != 0);

        bool rValue = mServer.logout(client->id());

        CLogoutResponseEvent response(pActionRequest->transactionId(), rValue);
        client->sendResponse(&response);
        break;
    }
    case CEvent::CreateRoomRequest:
    {
        const CCreateRoomRequestEvent *createRoomRequestEvent =
                dynamic_cast<const CCreateRoomRequestEvent*>(pActionRequest);

        std::cout << "Server Stub: Create Room Request received" << std::endl;

        CClient *client = mServer.clients().client(pConnection);
        assert(client != 0);

        bool rValue = mServer.createRoom(client->id(),
            createRoomRequestEvent->roomName(),
            createRoomRequestEvent->roomType(),
            createRoomRequestEvent->password());

        CCreateRoomResponseEvent response(pActionRequest->transactionId(),
            rValue);
        client->sendResponse(&response);
        break;
    }
    case CEvent::JoinRoomRequest:
    {
        const CJoinRoomRequestEvent *joinRoomRequestEvent =
                dynamic_cast<const CJoinRoomRequestEvent*>(pActionRequest);

        std::cout << "Server Stub: Join Room Request received" << std::endl;

        CClient *client = mServer.clients().client(pConnection);
        assert(client != 0);

        bool rValue = mServer.joinRoom(client->id(),
                joinRoomRequestEvent->roomId(),
                joinRoomRequestEvent->password());

        CJoinRoomResponseEvent response(pActionRequest->transactionId(),
                rValue);
        client->sendResponse(&response);
        break;
    }
    case CEvent::LeaveRoomRequest:
    {
        const CLeaveRoomRequestEvent *leaveRoomRequestEvent =
                dynamic_cast<const CLeaveRoomRequestEvent*>(pActionRequest);

        std::cout << "Server Stub: Leave Room Request received" << std::endl;

        CClient *client = mServer.clients().client(pConnection);
        assert(client != 0);

        bool rValue = mServer.leaveRoom(client->id(),
                leaveRoomRequestEvent->roomId());

        CLeaveRoomResponseEvent response(pActionRequest->transactionId(),
                                         rValue);
        client->sendResponse(&response);
        break;
    }
    case CEvent::AddObjectRequest:
    {
        std::cout << "Server Stub: Add Object Request received" << std::endl;

        const CAddItemRequestEvent *addItemRequestEvent =
                dynamic_cast<const CAddItemRequestEvent*>(pActionRequest);

        CClient *client = mServer.clients().client(pConnection);
        assert(client != 0);

        greenhills::core::CItemId itemId = mServer.addItem(client->id(),
                addItemRequestEvent->roomId(), addItemRequestEvent->itemName(),
                addItemRequestEvent->itemType());

        CAddItemResponseEvent response(pActionRequest->transactionId(), itemId);
        client->sendResponse(&response);
        break;
    }
    case CEvent::RemoveObjectRequest:
    {
        std::cout << "Server Stub: Remove Object Request received" << std::endl;

        const CRemoveItemRequestEvent *removeItemRequestEvent =
                dynamic_cast<const CRemoveItemRequestEvent*>(pActionRequest);

        CClient *client = mServer.clients().client(pConnection);
        assert(client != 0);

        bool succeeded = mServer.removeItem(client->id(),
                removeItemRequestEvent->roomId(),
                removeItemRequestEvent->itemId());

        CRemoveItemResponseEvent response(
                pActionRequest->transactionId(), succeeded);
        client->sendResponse(&response);
        break;
    }
    case CEvent::UpdateObjectRequest:
    {
        const CUpdateObjectRequestEvent *updateObjectRequestEvent =
                dynamic_cast<const CUpdateObjectRequestEvent*>(pActionRequest);

        std::cout << "Server Stub: Update Object Request received" << std::endl;

        CClient *client = mServer.clients().client(pConnection);
        assert(client != 0);

        bool rValue = mServer.updateItem(client->id(),
                updateObjectRequestEvent->roomId(),
                updateObjectRequestEvent->itemId(),
                updateObjectRequestEvent->data(),
                updateObjectRequestEvent->length());

        CUpdateObjectResponseEvent response(pActionRequest->transactionId(),
                rValue);
        client->sendResponse(&response);
        break;
    }
    default:
        std::cout << "Unsupported action type" << std::endl;
    }
}

void CStub::eventReceived(const CEvent *pEvent,
                                CConnection *pConnection)
{
    Q_UNUSED(pConnection);
    switch(pEvent->type())
    {
    case CEvent::IdentificationEvent:
    {
        const CIdentificationEvent *identificationEvent =
                dynamic_cast<const CIdentificationEvent*>(pEvent);

        std::cout << "Identification Event received" << std::endl;
        std::cout << "   ClientId: " << identificationEvent->clientId()
                << std::endl;

        CClient *client =
                mServer.clients().client(identificationEvent->clientId());
        if (client)
        {
            assert(client);
            client->assignEventConnection(pConnection);
            std::cout << "Event Channel Assigned" << std::endl;
        }
        else
        {
            std::cout << "Event Channel assignment error. <Invalid ClientId>"
                    << std::endl;
        }
        break;
    }
    default:
        std::cout << "Unsupported event type" << std::endl;
    }
}
